/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.gcm.demo.server;

import antlr.Parser;

import com.google.android.gcm.server.Constants;
import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.MulticastResult;
import com.google.android.gcm.server.Result;
import com.google.android.gcm.server.Sender;
import com.hs.mobile.model.Category;
import com.hs.mobile.model.Device;
import com.hs.mobile.model.Notifications;
import com.hs.mobile.model.ServiceMB;
import com.hs.mobile.service.CategoryService;
import com.hs.mobile.service.device.DeviceService;
import com.hs.mobile.service.notifications.NotificationsService;

import java.io.IOException;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * Servlet that adds a new message to all registered devices.
 * <p>
 * This servlet is used just by the browser (i.e., not device).
 */
@SuppressWarnings("serial")
public class SendAllMessagesServlet extends BaseServlet {

  private static final int MULTICAST_SIZE = 1000;
  private static final Logger logger = Logger.getLogger("sendMessage");

  private Sender sender;

  private static final Executor threadPool = Executors.newFixedThreadPool(5);
  
  @Override
  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    sender = newSender(config);
  }

  /**
   * Creates the {@link Sender} based on the servlet settings.
   */
  protected Sender newSender(ServletConfig config) {
    String key = (String) config.getServletContext()
        .getAttribute(ApiKeyInitializer.ATTRIBUTE_ACCESS_KEY);
    return new Sender(key);
  }

  private static DeviceService deviceService;
  private static NotificationsService notificationService ;
  private static DeviceService getListRegisteredDevice() {
	  if( deviceService == null ) {
		  ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		  deviceService = (DeviceService)appContext.getBean("deviceService");
	  }
	  return deviceService;
  }
  private static NotificationsService getListNotificationDevice() {
	  if( notificationService == null ) {
		  ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		  notificationService = (NotificationsService)appContext.getBean("notificationService");
	  }
	  return notificationService;
  }
  /**
   * Processes the request to add a new message.
   */
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws IOException, ServletException {
	  String status = "Error";	 
	  // Begin add - 04/11/2012
	  
		ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");  	   	
		CategoryService categoryBo = (CategoryService)appContext.getBean("categoryService");
		List<Category> list = categoryBo.getAllCategories();
		
		DeviceService deviceService = ( DeviceService) appContext.getBean("deviceService");
		List<Device> listAvailableDevices = deviceService.getAllAvailableDevices();
		if( listAvailableDevices.isEmpty() ) 
		{
			status = status + "Message ignored as there is no device registered ";
		} else {
			if( listAvailableDevices.size() == 1 )
			{
				String registrationId = listAvailableDevices.get(0).getDeviceId();
		        Message message = new Message.Builder()
									.collapseKey("collapse_key")
									.timeToLive(3)
									.delayWhileIdle(true)
									.addData("key1", "value1")
									.addData("key2", "value2")
									.build();
		        Result result = null;	
		        Notifications notification = new Notifications();
		        int sentStatus  = 1;
		        try {
		        	result = sender.send(message, registrationId, 5);
		        	logger.debug(" Sent successfully " + result );		        	
				} catch (IOException e) {
					// TODO: handle exception
					logger.debug(" Sent error " + result );
					sentStatus = 0;
				}
		        long p = Long.parseLong(listAvailableDevices.get(0).getDeviceId());
		        List<ServiceMB> listService = getListRegisteredDevice().getListRegisteredDevice(p);
		        notification.setCountPush(1);
		        notification.setUpdateStatus(sentStatus);
		        notification.setLastPush(new Date() );
		        for( ServiceMB service : listService ) {
		        	notification.setServiceId(service.getId());
		        	getListNotificationDevice().save(notification);
		        }
		        status = status + "Sent message to one device: " + result + "abc";
		    } else if ( listAvailableDevices.size() > 1 ) {
				int total = listAvailableDevices.size();
		        List<String> partialDevices = new ArrayList<String>(total);
		        int counter = 0;
		        int tasks = 0;
		        for (Device device : listAvailableDevices) {
		          counter++;
		          partialDevices.add(device.getDeviceId());
		          int partialSize = partialDevices.size();
		          if (partialSize == MULTICAST_SIZE || counter == total) {
		            asyncSend(partialDevices);
		            partialDevices.clear();
		            tasks++;
		          }
		        }
		        status = "Asynchronously sending " + tasks + " multicast messages to " +  total + " devices";
			}
		}
	req.setAttribute(HomeServlet.ATTRIBUTE_STATUS, status.toString());
    getServletContext().getRequestDispatcher("/home").forward(req, resp);
  }  	
	  // End add - 04/11/2012  
  private void asyncSend(List<String> partialDevices) {
    // make a copy
    final List<String> devices = new ArrayList<String>(partialDevices);
    threadPool.execute(new Runnable() {

      public void run() {
        Message message = new Message.Builder().build();
							//.collapseKey("collapse_key")
							//.timeToLive(3)
							//.delayWhileIdle(true)
							//.addData("key1", "value1")
							//.addData("key2", "value2")
							//.build();
        MulticastResult multicastResult;
        try {
          multicastResult = sender.send(message, devices, 5);
        } catch (IOException e) {
          logger.debug("Error posting messages", e);
          return;
        }
        List<Result> results = multicastResult.getResults();
        for (int i = 0; i < devices.size(); i++) {
          String regId = devices.get(i);
          Result result = results.get(i);
          String messageId = result.getMessageId();
          if (messageId != null) {
            logger.debug("Succesfully sent message to device: " + regId +  "; messageId = " + messageId);
            String canonicalRegId = result.getCanonicalRegistrationId();
            if (canonicalRegId != null) {
              logger.info("canonicalRegId " + canonicalRegId);
              Datastore.updateRegistration(regId, canonicalRegId);
            }
          } else {
            String error = result.getErrorCodeName();
            if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
              logger.debug("Unregistered device: " + regId);
            } else {
              logger.debug("Error sending message to " + regId + ": " + error);
            }
          }
        }
      }});
  }

}
